Introduction¶

In this notebook we'll learn how to use NumPy to work with numerical data.

Import Statements¶

In [5]:
# !pip install scipy
import numpy as np

import matplotlib.pyplot as plt
from scipy import misc # contains an image of a racoon!
from PIL import Image # for reading image files

Understanding NumPy's ndarray¶

NumPy's most amazing feature is the powerful ndarray.

1-Dimensional Arrays (Vectors)¶

In [6]:
my_array = np.array([1.1, 9.2, 8.1, 4.7])
my_array.shape
Out[6]:
(4,)
In [9]:
my_array.ndim
Out[9]:
1

2-Dimensional Arrays (Matrices)¶

In [13]:
array_2d = np.array([[1, 2, 3, 9], 
                     [5, 6, 7, 8]])
In [18]:
array_2d[0, :]
Out[18]:
array([1, 2, 3, 9])
In [19]:
array_2d[:, 0]
Out[19]:
array([1, 5])
In [14]:
print(f'array_2d has {array_2d.ndim} dimensions')
print(f'Its shape is {array_2d.shape}')
print(f'It has {array_2d.shape[0]} rows and {array_2d.shape[1]} columns')
print(array_2d)
array_2d has 2 dimensions
Its shape is (2, 4)
It has 2 rows and 4 columns
[[1 2 3 9]
 [5 6 7 8]]

N-Dimensional Arrays (Tensors)¶

Challenge:

  • How many dimensions does the array below have?
  • What is its shape (i.e., how many elements are along each axis)?
  • Try to access the value 18 in the last line of code.
  • Try to retrieve a 1 dimensional vector with the values [97, 0, 27, 18]
  • Try to retrieve a (3,2) matrix with the values [[ 0, 4], [ 7, 5], [ 5, 97]]

Hint: You can use the : operator just as with Python Lists.

In [15]:
mystery_array = np.array([[[0, 1, 2, 3],
                           [4, 5, 6, 7]],
                        
                         [[7, 86, 6, 98],
                          [5, 1, 0, 4]],
                          
                          [[5, 36, 32, 48],
                           [97, 0, 27, 18]]])

# Note all the square brackets!
In [21]:
mystery_array.ndim
Out[21]:
3
In [22]:
mystery_array.shape
Out[22]:
(3, 2, 4)
In [28]:
mystery_array[2, 1, 3]
Out[28]:
18
In [29]:
mystery_array[2, 1]
Out[29]:
array([97,  0, 27, 18])
In [42]:
mystery_array[:, :, 0]
Out[42]:
array([[ 0,  4],
       [ 7,  5],
       [ 5, 97]])

NumPy Mini-Challenges¶

Challenge 1: Use .arange()to createa a vector a with values ranging from 10 to 29. You should get this:¶

print(a)

[10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29]

In [44]:
a = np.arange(10,30)
print(a)
[10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29]

Challenge 2: Use Python slicing techniques on a to:¶

  • Create an array containing only the last 3 values of a
  • Create a subset with only the 4th, 5th, and 6th values
  • Create a subset of a containing all the values except for the first 12 (i.e., [22, 23, 24, 25, 26, 27, 28, 29])
  • Create a subset that only contains the even numbers (i.e, every second number)
In [52]:
a[-3:]
Out[52]:
array([27, 28, 29])
In [60]:
a[3:6]
Out[60]:
array([13, 14, 15])
In [63]:
a[12:]
Out[63]:
array([22, 23, 24, 25, 26, 27, 28, 29])
In [67]:
a[::2]
Out[67]:
array([10, 12, 14, 16, 18, 20, 22, 24, 26, 28])

Challenge 3:Reverse the order of the values in a, so that the first element comes last:¶

[29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10]

If you need a hint, you can check out this part of the NumPy beginner's guide

In [68]:
np.flip(a)
Out[68]:
array([29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13,
       12, 11, 10])

Challenge 4: Print out all the indices of the non-zero elements in this array: [6,0,9,0,0,5,0]¶

In [69]:
b = [6,0,9,0,0,5,0]
In [70]:
np.nonzero(b)
Out[70]:
(array([0, 2, 5]),)

Challenge 5: Use NumPy to generate a 3x3x3 array with random numbers¶

Hint: Use the .random() function

In [74]:
from numpy.random import random
random((3,3,3))
Out[74]:
array([[[0.40985817, 0.45258454, 0.88497001],
        [0.02560356, 0.2239888 , 0.98561234],
        [0.81811221, 0.02305315, 0.33259366]],

       [[0.44806376, 0.05391684, 0.29878516],
        [0.04332292, 0.80126497, 0.24534011],
        [0.30860801, 0.90281948, 0.41889011]],

       [[0.26148218, 0.69495833, 0.91607231],
        [0.15823933, 0.36314199, 0.66732195],
        [0.5950032 , 0.83777732, 0.62619449]]])

Challenge 6: Use .linspace() to create a vector x of size 9 with values spaced out evenly between 0 to 100 (both included).¶

In [77]:
x = np.linspace(0, 100, num=9)
x
Out[77]:
array([  0. ,  12.5,  25. ,  37.5,  50. ,  62.5,  75. ,  87.5, 100. ])

Challenge 7: Use .linspace() to create another vector y of size 9 with values between -3 to 3 (both included). Then plot x and y on a line chart using Matplotlib.¶

In [79]:
y = np.linspace(-3, 3, 9)
y
Out[79]:
array([-3.  , -2.25, -1.5 , -0.75,  0.  ,  0.75,  1.5 ,  2.25,  3.  ])
In [81]:
plt.plot(x,y)
plt.show
Out[81]:
<function matplotlib.pyplot.show(close=None, block=None)>

Challenge 8: Use NumPy to generate an array called noise with shape 128x128x3 that has random values. Then use Matplotlib's .imshow() to display the array as an image.¶

In [85]:
noise = np.random.random((128, 128, 3))
plt.imshow(noise)
Out[85]:
<matplotlib.image.AxesImage at 0x7ff8f40090c0>

Linear Algebra with Vectors¶

In [86]:
v1 = np.array([4, 5, 2, 7])
v2 = np.array([2, 1, 3, 3])
In [87]:
v1 + v2
Out[87]:
array([ 6,  6,  5, 10])
In [90]:
v1 * v2
Out[90]:
array([ 8,  5,  6, 21])
In [88]:
# Python Lists vs ndarrays
list1 = [4, 5, 2, 7]
list2 = [2, 1, 3, 3]
In [89]:
list1 + list2
Out[89]:
[4, 5, 2, 7, 2, 1, 3, 3]
In [91]:
list1 * list2
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[91], line 1
----> 1 list1 * list2

TypeError: can't multiply sequence by non-int of type 'list'

Broadcasting and Scalars¶

In [97]:
array_2d = np.array([[1, 2, 3, 4], 
                     [5, 6, 7, 8]])
In [98]:
array_2d + 6
Out[98]:
array([[ 7,  8,  9, 10],
       [11, 12, 13, 14]])
In [99]:
array_2d * 6
Out[99]:
array([[ 6, 12, 18, 24],
       [30, 36, 42, 48]])

Matrix Multiplication with @ and .matmul()¶

In [101]:
a1 = np.array([[1, 3],
               [0, 1],
               [6, 2],
               [9, 7]])

b1 = np.array([[4, 1, 3],
               [5, 8, 5]])

print(f'{a1.shape}: a has {a1.shape[0]} rows and {a1.shape[1]} columns.')
print(f'{b1.shape}: b has {b1.shape[0]} rows and {b1.shape[1]} columns.')
print('Dimensions of result: (4x2)*(2x3)=(4x3)')
(4, 2): a has 4 rows and 2 columns.
(2, 3): b has 2 rows and 3 columns.
Dimensions of result: (4x2)*(2x3)=(4x3)

Challenge: Let's multiply a1 with b1. Looking at the wikipedia example above, work out the values for c12 and c33 on paper. Then use the .matmul() function or the @ operator to check your work.

In [104]:
np.matmul(a1, b1)
Out[104]:
array([[19, 25, 18],
       [ 5,  8,  5],
       [34, 22, 28],
       [71, 65, 62]])
In [105]:
a1 @ b1
Out[105]:
array([[19, 25, 18],
       [ 5,  8,  5],
       [34, 22, 28],
       [71, 65, 62]])

Manipulating Images as ndarrays¶

In [125]:
img = misc.face()
plt.imshow(img)
/tmp/ipykernel_113772/1403994607.py:1: DeprecationWarning: scipy.misc.face has been deprecated in SciPy v1.10.0; and will be completely removed in SciPy v1.12.0. Dataset methods have moved into the scipy.datasets module. Use scipy.datasets.face instead.
  img = misc.face()
Out[125]:
<matplotlib.image.AxesImage at 0x7ff8f8985f60>

Challenge: What is the data type of img? Also, what is the shape of img and how many dimensions does it have? What is the resolution of the image?

In [129]:
print(f"Datatype: {type(img)}\nShape: {img.shape}(first 2 are resolution)\nDimensions: {img.ndim}")
Datatype: <class 'numpy.ndarray'>
Shape: (768, 1024, 3)(first 2 are resolution)
Dimensions: 3
In [6]:
 
In [6]:
 
In [6]:
 

Challenge: Convert the image to black and white. The values in our img range from 0 to 255.

  • Divide all the values by 255 to convert them to sRGB, where all the values are between 0 and 1.
  • Next, multiply the sRGB array by the grey_vals to convert the image to grey scale.
  • Finally use Matplotlib's .imshow() together with the colormap parameter set to gray cmap=gray to look at the results.
In [131]:
grey_vals = np.array([0.2126, 0.7152, 0.0722])
In [132]:
sRGB_array = img / 255
In [135]:
grey_scale = np.matmul(sRGB_array, grey_vals)
In [139]:
plt.imshow(grey_scale, cmap='gray')
Out[139]:
<matplotlib.image.AxesImage at 0x7ff8d1d28700>

Challenge: Can you manipulate the images by doing some operations on the underlying ndarrays? See if you can change the values in the ndarray so that:

  1. You flip the grayscale image upside down
  1. Rotate the colour image
  1. Invert (i.e., solarize) the colour image. To do this you need to converting all the pixels to their "opposite" value, so black (0) becomes white (255).

Challenge Solutions¶

In [141]:
plt.imshow(np.flip(grey_scale), cmap='gray')
Out[141]:
<matplotlib.image.AxesImage at 0x7ff8d1d2abf0>
In [142]:
plt.imshow(np.rot90(img))
Out[142]:
<matplotlib.image.AxesImage at 0x7ff8d1ae9480>
In [147]:
plt.imshow(255 - img)
Out[147]:
<matplotlib.image.AxesImage at 0x7ff8d0311240>

Use your Own Image!¶

In [150]:
file_name = 'yummy_macarons.jpg'
In [151]:
wall = 'wall.jpg'

Use PIL to open¶

In [152]:
my_img = Image.open(file_name)
In [153]:
img_array = np.array(my_img)
In [154]:
plt.imshow(img_arrayp
Out[154]:
<matplotlib.image.AxesImage at 0x7ff8d1d4b4c0>
In [156]:
plt.imshow(255 - img_array)
Out[156]:
<matplotlib.image.AxesImage at 0x7ff8d1d1f430>
In [157]:
wall_img = Image.open(wall)
In [158]:
wall_array = np.array(wall_img)
In [160]:
plt.imshow(wall_array)
Out[160]:
<matplotlib.image.AxesImage at 0x7ff8d02eee60>
In [161]:
plt.imshow(255 - wall_array)
Out[161]:
<matplotlib.image.AxesImage at 0x7ff8cfed04f0>
In [168]:
grey_wall = np.matmul(wall_array/255, grey_vals)
In [169]:
plt.imshow(grey_wall)
Out[169]:
<matplotlib.image.AxesImage at 0x7ff8cf8de590>
In [172]:
plt.imshow(255 - grey_wall)
Out[172]:
<matplotlib.image.AxesImage at 0x7ff8cf79c460>
In [173]:
plt.imshow(grey_wall, cmap='gray')
Out[173]:
<matplotlib.image.AxesImage at 0x7ff8cf769510>
In [ ]: